package com.demo.config;

import java.sql.SQLException;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import com.demo.enums.ResponseStatus;
import com.demo.exception.BaseException;
import com.demo.web.response.Response;

import lombok.extern.slf4j.Slf4j;

/**
 * 基本的全局异常处理器
 * 
 *
 */
@Slf4j
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@SuppressWarnings({"unused", "rawtypes"})
public class CommonExceptionController {

    // 通过Handler获取到Controller的返回值类型
    private ResponseEntity<Response> createResponse(HandlerMethod handler, ResponseStatus status, String message) {
        Response r = Response.error(status, message);
        return new ResponseEntity<>(r, HttpStatus.OK);
    }

    @ExceptionHandler(BaseException.class)
    public Response handleBaseException(HttpServletRequest req, HandlerMethod handler, BaseException e) {
        log.error("[" + e.getMessage() + "]", e);
        return Response.error(e.getStatus(), e.getMessage());
    }

    @ExceptionHandler(Throwable.class)
    public Response handleThrowable(HttpServletRequest req, HandlerMethod handler, Throwable e) {
        log.error("[" + e.getMessage() + "]", e);
        return Response.error(ResponseStatus.ERROR, ResponseStatus.ERROR.getMessage());
    }


    /**
     * JSON方式  @Valid or @Validated 抛出的异常
     * 
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Response methodArgumentNotValidExceptionHandler(HttpServletRequest req, HandlerMethod handler,
        MethodArgumentNotValidException e) {
        log.error("[MethodArgumentNotValidException] ", e);
        StringBuilder message = new StringBuilder();
        for (ObjectError error : e.getBindingResult().getAllErrors()) {
            message.append(error.getDefaultMessage()).append("\n");
        }
        return Response.error(ResponseStatus.VALID_ERROR, message.toString());
    }

    /**
     * form data 方式接受参数 @Valid
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler(BindException.class)
    public Response bindException(BindException e, HttpServletRequest request) {
        log.error("[BindException] ", e);
        try {
            String msgs = e.getBindingResult().getFieldError().getDefaultMessage();
            if (StringUtils.hasText(msgs)) {
                return Response.error(ResponseStatus.PARAM_ERROR, msgs);
            }
        } catch (Exception ee) {}
        StringBuilder msg = new StringBuilder();
        List<FieldError> fieldErrors = e.getFieldErrors();
        fieldErrors.forEach((oe) -> msg.append("参数:[").append(oe.getObjectName()).append(".").append(oe.getField())
            .append("]的传入值:[").append(oe.getRejectedValue()).append("]与预期的字段类型不匹配."));
        return Response.error(ResponseStatus.PARAM_ERROR, msg.toString());
    }

    /**
     * jsr 规范中的验证异常 单个参数校验异常抛出ConstraintViolationException
     * 
     * @param e
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Response constraintViolationException(ConstraintViolationException e, HttpServletRequest request) {
        log.error("[ConstraintViolationException]", e);
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        String message = violations.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(";"));
        return Response.error(ResponseStatus.VALID_ERROR, message);
    }
    /**
     * 参数不存在
     * 
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Response httpMessageNotReadableException(HttpMessageNotReadableException e, HttpServletRequest request) {
        log.error("[HttpMessageNotReadableException] ", e);
        String message = e.getMessage();
        String msg = "参数解析失败";
        if (StringUtils.substringMatch(message, 0, "Could not read document:")) {
            int index = message.indexOf("Could not read document:");
            int index2 = message.indexOf(" at ");
            msg = String.format("无法正确的解析json类型的参数：%s", message.substring(index + 24, index2));
        }
        return Response.error(ResponseStatus.PARAM_ERROR, msg);
    }

    /**
     * 参数类型不正确不能解析
     * 
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public Response methodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e,
        HttpServletRequest request) {
        log.error("[MethodArgumentTypeMismatchException] ", e);
        MethodArgumentTypeMismatchException eee = (MethodArgumentTypeMismatchException)e;
        StringBuilder msg = new StringBuilder("参数：[").append(eee.getName()).append("]的传入值：[").append(eee.getValue())
            .append("]与预期的字段类型：[").append(eee.getRequiredType().getName()).append("]不匹配");
        return Response.error(ResponseStatus.PARAM_ERROR, msg.toString());
    }

    /**
     * 缺少必须的url参数 @RequestParam 注解
     * 
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Response missingServletRequestParameterException(MissingServletRequestParameterException e,
        HttpServletRequest request) {
        log.error("[MissingServletRequestParameterException]", e);
        StringBuilder msg = new StringBuilder();
        msg.append("缺少必须的[").append(e.getParameterType()).append("]类型的参数[").append(e.getParameterName()).append("]");
        return Response.error(ResponseStatus.PARAM_ERROR, msg.toString());
    }

  

    @ExceptionHandler(SQLException.class)
    public Response sqlException(SQLException e, HttpServletRequest request) {
        log.error("[SQLException]", e);
        return Response.error(ResponseStatus.SQL_ERROR);
    }

    @ExceptionHandler(DataAccessException.class)
    public Response dataIntegrityViolationException(DataAccessException e, HttpServletRequest request) {
        log.error("[DataIntegrityViolationException]", e);
        return Response.error(ResponseStatus.SQL_ERROR);
    }

}
